/* * Copyright (c) 2012. HappyDroids LLC, All rights reserved. */ package net.kencochrane.sentry; import org.apach3.commons.lang3.time.DateFormatUtils; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.SignatureException; import java.util.Date; import java.util.UUID; import java.util.zip.CRC32; import java.util.zip.Checksum; import java.util.zip.GZIPOutputStream; import static org.apach3.commons.codec.binary.Base64.encodeBase64String; /** * User: ken Cochrane * Date: 2/10/12 * Time: 10:43 AM * Utility class for the Raven client. */ public class RavenUtils { private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; /** * Computes RFC 2104-compliant HMAC signature. * Based off of the sample here. http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AuthJavaSampleHMACSignature.html * * @param data The data to be signed. * @param key The signing key. * @return The hex-encoded RFC 2104-compliant HMAC signature. * @throws java.security.SignatureException * when signature generation fails */ public static String calculateHMAC(String data, String key) throws java.security.SignatureException { String result; try { // get an hmac_sha1 key from the raw key bytes SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM); // get an hmac_sha1 Mac instance and initialize with the signing key Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); mac.init(signingKey); // compute the hmac on input data bytes byte[] rawHmac = mac.doFinal(data.getBytes()); result = hexEncode(rawHmac); } catch (Exception e) { throw new SignatureException("Failed to generate HMAC : " + e.getMessage()); } return result; } /** * Get the hostname for the system throwing the error. * * @return The hostname for the server */ public static String getHostname() { String hostname; try { hostname = InetAddress.getLocalHost().getCanonicalHostName(); } catch (UnknownHostException e) { // can't get hostname hostname = "unavailable"; } return hostname; } /** * Convert a java date into the ISO8601 format YYYYMMDDTHH:mm:ss * in timezone UTC * * @param date the date to convert * @return ISO8601 formatted date as a String */ public static String getDateAsISO8601String(Date date) { return DateFormatUtils.formatUTC(date, DateFormatUtils.ISO_DATETIME_FORMAT.getPattern()); } /** * Get the timestamp for right now as a long * * @return timestamp for now as long */ public static long getTimestampLong() { return System.currentTimeMillis(); } /** * Given a long timestamp convert to a ISO8601 formatted date string * * @param timestamp the timestamp to convert * @return ISO8601 formatted date string */ public static String getTimestampString(long timestamp) { java.util.Date date = new java.util.Date(timestamp); return getDateAsISO8601String(date); } /** * Given the time right now return a ISO8601 formatted date string * * @return ISO8601 formatted date string */ public static String getTimestampString() { java.util.Date date = new java.util.Date(); return getDateAsISO8601String(date); } /** * build the HMAC sentry signature * <p/> * The header is composed of a SHA1-signed HMAC, the timestamp from when the message was generated, * and an arbitrary client version string. * <p/> * The client version should be something distinct to your client, and is simply for reporting purposes. * To generate the HMAC signature, take the following example (in Python): * <p/> * hmac.new(public_key, '%s %s' % (timestamp, message), hashlib.sha1).hexdigest() * * @param message the error message to send to sentry * @param timestamp the timestamp for when the message was created * @param key sentry public key * @return SHA1-signed HMAC string */ public static String getSignature(String message, long timestamp, String key) { String full_message = timestamp + " " + message; String hmac = null; try { hmac = calculateHMAC(full_message, key); } catch (SignatureException e) { e.printStackTrace(); } return hmac; } /** * The byte[] returned by MessageDigest does not have a nice * textual representation, so some form of encoding is usually performed. * <p/> * This implementation follows the example of David Flanagan's book * "Java In A Nutshell", and converts a byte array into a String * of hex characters. * <p/> * Another popular alternative is to use a "Base64" encoding. * * @param aInput what we are hex encoding * @return hex encoded string. */ public static String hexEncode(byte[] aInput) { StringBuilder result = new StringBuilder(); char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; for (byte b : aInput) { result.append(digits[(b & 0xf0) >> 4]); result.append(digits[b & 0x0f]); } return result.toString(); } /** * An almost-unique hash identifying the this event to improve aggregation. * * @param message The message we are sending to sentry * @return CRC32 Checksum string */ public static String calculateChecksum(String message) { // get bytes from string byte bytes[] = message.getBytes(); Checksum checksum = new CRC32(); // update the current checksum with the specified array of bytes checksum.update(bytes, 0, bytes.length); // get the current checksum value long checksumValue = checksum.getValue(); return String.valueOf(checksumValue); } /** * Hexadecimal string representing a uuid4 value. * * @return Hexadecimal UUID4 String */ public static String getRandomUUID() { UUID uuid = UUID.randomUUID(); String uuid_string = uuid.toString(); // if we keep the -'s in the uuid, it is too long, remove them uuid_string = uuid_string.replaceAll("-", ""); return uuid_string; } /** * Gzip then base64 encode the str value * * @param str the value we want to compress and encode. * @return Base64 encoded compressed version of the string passed in. */ public static String compressAndEncode(String str) { if (str == null || str.length() == 0) { return str; } ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gzip; try { gzip = new GZIPOutputStream(out); gzip.write(str.getBytes()); gzip.close(); } catch (IOException e) { e.printStackTrace(); //todo do something here better then this. } return encodeBase64String(out.toByteArray()); } }